package com.hzw.saas.service.hpc.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.PageUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hzw.saas.api.hpc.*;
import com.hzw.saas.api.hpc.bo.*;
import com.hzw.saas.api.hpc.enums.DisplayUnit;
import com.hzw.saas.common.config.util.MybatisPlusUtil;
import com.hzw.saas.common.config.util.PageParam;
import com.hzw.saas.common.config.util.PageUtils;
import com.hzw.saas.common.util.Arith;
import com.hzw.saas.common.util.LocalDateTimeUtils;
import com.hzw.saas.common.util.MapperUtil;
import com.hzw.saas.service.hpc.mapper.HpcAccountDetailMapper;
import com.hzw.saas.service.hpc.model.HpcAccountDetail;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import ma.glasnost.orika.MapperFacade;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 用户机时明细表 服务实现类
 * </p>
 *
 * @author zzl
 * @since 2021-02-05
 */
@Slf4j
@Service("hpcAccountDetailServiceImpl")
@RequiredArgsConstructor
public class HpcAccountDetailServiceImpl extends ServiceImpl<HpcAccountDetailMapper, HpcAccountDetail> implements IHpcAccountDetailService, IService<HpcAccountDetail> {

    private final MapperFacade mapperFacade;

    @Resource(name = "hpcAccountUserServiceImpl")
    private IHpcAccountUserService hpcAccountUserService;
    @Resource(name = "hpcAccountRecordServiceImpl")
    private IHpcAccountRecordService hpcAccountRecordService;
    @Resource(name = "hpcAccountIncreaseImpl")
    private IHpcAccountIncreaseService hpcAccountIncreaseService;
    @Resource(name = "hpcAccountAmountDailyServiceImpl")
    private IHpcAccountAmountDailyService hpcAccountAmountDailyService;

    @Override
    public double countHpcAccountDetailByUser(String userId, String hpcAccountUserId) {
        if (StringUtils.isBlank(userId)) {
            return 0;
        }

        double coreTimeSum = 0;
        HpcAccountUserBo accountUserBo = hpcAccountUserService.getWithRecord(hpcAccountUserId);
        if (Objects.isNull(accountUserBo) || !Objects.equals(userId, accountUserBo.getUserId())) {
            return 0;
        }

        List<HpcAccountDetail> list1 = this.lambdaQuery()
            .eq(HpcAccountDetail::getHpcAccountRecordId, accountUserBo.getHpcAccountRecordId()).list();
        for (HpcAccountDetail hpcAccountDetail : list1) {
            coreTimeSum = Arith.add(hpcAccountDetail.getHpcCoreTime(), coreTimeSum);
        }

        return coreTimeSum;
    }

    @Override
    public HpcAccountDetailBo saveHpcAccountDetail(HpcAccountDetailBo hpcAccountDetailDto) {
        if (hpcAccountDetailDto == null) {
            return null;
        }
        if (hpcAccountDetailDto.getRecordTime() == null) {
            hpcAccountDetailDto.setRecordTime(new Date());
        }
        HpcAccountDetail hpcAccountDetail = mapperFacade.map(hpcAccountDetailDto, HpcAccountDetail.class);
        this.save(hpcAccountDetail);
        return mapperFacade.map(hpcAccountDetail, HpcAccountDetailBo.class);
    }

    @Override
    public IPage<HpcAccountDetailBo> listHpcAccountDetail(PageParam pageParam, List<String> accountRecordIds, Date startDate, Date endDate) {
        if (pageParam == null) {
            pageParam = new PageParam();
        }
        if (CollectionUtil.isEmpty(accountRecordIds))
            return new Page<>();
        convertDateQuery(startDate, endDate);
        IPage<HpcAccountDetail> page = this.lambdaQuery()
            .in(accountRecordIds.size() > 1, HpcAccountDetail::getHpcAccountRecordId, accountRecordIds)
            .eq(accountRecordIds.size() == 1, HpcAccountDetail::getHpcAccountRecordId, accountRecordIds.get(0))
            .ge(startDate != null, HpcAccountDetail::getStartTime, startDate)
            .le(endDate != null, HpcAccountDetail::getStartTime, endDate)
            .orderByDesc(HpcAccountDetail::getEndTime)
            .page(pageParam.convert());
        IPage<HpcAccountDetailBo> dtoIPage = PageUtils.convert(page, HpcAccountDetailBo.class);
        for (HpcAccountDetailBo record : dtoIPage.getRecords()) {
            HpcAccountRecordBo hpcAccountUserBo = hpcAccountRecordService.get(record.getHpcAccountRecordId());
            record.setHpcAccount(hpcAccountUserBo.getHpcAccountSnapshot());
            record.setHpcCoreTime(NumberUtil.round(record.getHpcCoreTime(), 2).doubleValue());
            record.onReady();
        }
        return dtoIPage;
    }

    @Override
    public List<HpcAccountUsageBo> listHpcAccountUsage(List<String> accountUserIds, int offsetDays, DisplayUnit displayUnit) {
        Date endDate = new Date();
        Date startDate = DateUtil.offsetDay(endDate, -offsetDays + 1);
        return listHpcAccountUsage(accountUserIds, startDate, endDate, DisplayUnit.D);
    }

    @Override
    public void saveBatch(List<HpcAccountDetailBo> hpcAccountDetails) {
        this.saveBatch(mapperFacade.mapAsList(hpcAccountDetails, HpcAccountDetail.class));
    }

    @Override
    public boolean saveBatchByTaskIdAndAccountIdAndTime(List<HpcAccountDetailBo> hpcAccountDetailBos) {
        boolean result = true;

        // 获取所有的recordId，并去重
        List<String> recordIdList = hpcAccountDetailBos.stream().map(HpcAccountDetailBo::getHpcAccountRecordId).distinct().collect(Collectors.toList());
        if (CollectionUtils.isEmpty(recordIdList)) return true;
        // 将recordId对应的明细都取出来
        List<HpcAccountDetail> hpcAccountDetails = this.list(Wrappers.<HpcAccountDetail>lambdaQuery()
            .in(recordIdList.size() > 1, HpcAccountDetail::getHpcAccountRecordId, recordIdList)
            .eq(recordIdList.size() == 1, HpcAccountDetail::getHpcAccountRecordId, recordIdList.get(0)));

        List<HpcAccountDetailBo> hadbs = new ArrayList<>();
        for (HpcAccountDetailBo accountDetailBo : hpcAccountDetailBos) {
            HpcAccountDetail hpcAccountDetail = null;
            for (HpcAccountDetail accountDetail : hpcAccountDetails) {
                if (Objects.equals(accountDetail.getHpcAccountRecordId(), accountDetailBo.getHpcAccountRecordId()) &&
                    Objects.equals(accountDetail.getTaskId(), accountDetailBo.getTaskId())) {
                    hpcAccountDetail = accountDetail;
                    break;
                }
            }

            if (hpcAccountDetail == null) {
                hadbs.add(accountDetailBo);
            } else {
                // 查询到了，判断 拉取的明细endTime  大于  数据存的 endTime  则更新
                if (DateUtil.compare(accountDetailBo.getEndTime(), hpcAccountDetail.getEndTime()) > 0 ||
                    // 起始时间小于数据库的时间则更新
                    DateUtil.compare(accountDetailBo.getStartTime(), hpcAccountDetail.getStartTime()) < 0 ||
                    // 如果拉取的endTime等于数据库的endTime则判断其他属性是否相等
                    (DateUtil.compare(accountDetailBo.getEndTime(), hpcAccountDetail.getEndTime()) == 0 &&
                        !Objects.equals(hpcAccountDetail.getTaskResult(), accountDetailBo.getTaskResult()))
                ) {
                    HpcAccountDetail hpcAccountDetail1 = mapperFacade.map(accountDetailBo, HpcAccountDetail.class);
                    log.info("update hpc detail before: {}", hpcAccountDetail);
                    log.info("update hpc detail after: {}", hpcAccountDetail1);
                    try {
                        this.update(hpcAccountDetail1, Wrappers.<HpcAccountDetail>lambdaQuery()
                            .eq(HpcAccountDetail::getHpcAccountRecordId, accountDetailBo.getHpcAccountRecordId())
                            .eq(HpcAccountDetail::getTaskId, accountDetailBo.getTaskId()));
                    } catch (Exception e) {
                        log.error("更新HPC明细失败", e);
                        result = false;
                    }
                }
            }
        }

        log.info("add new hpc detail size: " + hadbs.size());
        if (CollectionUtil.isNotEmpty(hadbs)) {
            try {
                this.saveBatch(hadbs);
                log.info("add new hpc detail success");
            } catch (Exception e) {
                log.error("插入HPC明细失败", e);
                result = false;
            }
        }

        return result;
    }

    @Override
    public Double getHpcCoreSumByScopeDate(List<String> accountIds, Date startDate, Date endDate) {
        if (Objects.isNull(startDate)) startDate = DateUtil.parseDate("2000-01-01");
        if (Objects.isNull(endDate)) endDate = DateUtil.beginOfDay(DateUtil.offsetDay(new Date(), 1));
        List<HpcAccountUsageBo> hpcAccountUsageBos = this.listHpcAccountUsage(accountIds, startDate, endDate, DisplayUnit.D);
        if (CollectionUtils.isEmpty(hpcAccountUsageBos)) return 0d;
        return hpcAccountUsageBos.stream().mapToDouble(HpcAccountUsageBo::getUsage).sum();
    }

    @Override
    public Double getHpcAccountCoreTimeSums(List<String> accountRecordIds) {
        if (CollectionUtil.isEmpty(accountRecordIds)) return 0d;

        QueryWrapper<HpcAccountDetail> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("IFNULL(sum(hpc_core_time),0) as hpcCoreTime").lambda().in(HpcAccountDetail::getHpcAccountRecordId, accountRecordIds);

        HpcAccountDetail hpcAccountDetail = this.getOne(queryWrapper);
        return hpcAccountDetail.getHpcCoreTime();
    }

    @Override
    public Double getHpcAccountCoreTimeSum(String accountRecordId) {
        return getHpcAccountCoreTimeSums(Collections.singletonList(accountRecordId));
    }

    @Override
    public List<HpcAccountUsageBo> listHpcAccountUsage(List<String> accountRecordIds, Date startDate, Date endDate, DisplayUnit displayUnit) {
        if (CollectionUtils.isEmpty(accountRecordIds)) {
            return new ArrayList<>();
        }

        if (DisplayUnit.H != displayUnit) {
            convertDateQuery(startDate, endDate);
        }

        if (startDate == null) {
            startDate = getBeginOffsetTime(displayUnit, new Date(), 0);
        } else if (DisplayUnit.H == displayUnit) {
            startDate = getBeginOffsetTime(displayUnit, startDate, 0);
        }

        if (endDate == null) {
            endDate = getEndOffsetTime(displayUnit, new Date(), 0);
        } else if (DisplayUnit.H == displayUnit) {
            endDate = getBeginOffsetTime(displayUnit, endDate, 1);
        }
        List<HpcAccountDetail> hpcAccountDetails = new ArrayList<>();

        if (accountRecordIds.size() <= MybatisPlusUtil.MAX_IN_PARAMS) {
            hpcAccountDetails = this.listDetailsByRecordIds(accountRecordIds, startDate, endDate);
        } else {
            List<List<String>> recordIdListGroup = ListUtil.partition(accountRecordIds, MybatisPlusUtil.MAX_IN_PARAMS);
            for (List<String> recordIds : recordIdListGroup) {
                hpcAccountDetails.addAll(this.listDetailsByRecordIds(recordIds, startDate, endDate));
            }
        }

        return splitHpcAccountDetails(hpcAccountDetails, startDate, endDate, displayUnit);
    }

    private List<HpcAccountDetail> listDetailsByRecordIds(List<String> recordIds, Date startDate, Date endDate) {
        return this.lambdaQuery()
            // 是否需要其他属性
//            .select(HpcAccountDetail::getStartTime, HpcAccountDetail::getEndTime, HpcAccountDetail::getHpcCoreTime, HpcAccountDetail::getHpcAccountRecordId, HpcAccountDetail::getHpcCore)
            .in(!recordIds.isEmpty(), HpcAccountDetail::getHpcAccountRecordId, recordIds)
            .le(HpcAccountDetail::getStartTime, endDate)
            .ge(HpcAccountDetail::getEndTime, startDate)
            .orderByAsc(HpcAccountDetail::getStartTime)
            .list();
    }

    /**
     * 分割hpc使用明细
     *
     * @param hpcAccountDetailList
     * @return
     */
    private List<HpcAccountUsageBo> splitHpcAccountDetails(List<HpcAccountDetail> hpcAccountDetailList, Date startDate, Date endDate, DisplayUnit displayUnit) {
        // 根据时间单位分割日期
        List<DateScope> dateScopes = splitByDateUnit(startDate, endDate, displayUnit);
        Map<Date, HpcAccountUsageBo> hpcAccountUsageBoMap = new LinkedHashMap<>();
        // 初始化各个时间段数据
        dateScopes.forEach(dateScope -> {
            Date s = dateScope.getS();
            Date e = dateScope.getE();
            hpcAccountUsageBoMap.put(s, new HpcAccountUsageBo(s, e, 0d));
        });
        // 分割HPC使用明细，并计算核时消耗
        List<HpcAccountUsageBo> HpcAccountUsageBos = new ArrayList<>();
        Iterator<HpcAccountDetail> iterator = hpcAccountDetailList.iterator();
        while (iterator.hasNext()) {
            HpcAccountDetail hpcAccountDetail = iterator.next();
            Double sumHpcCoreTime = hpcAccountDetail.getHpcCoreTime();
            Date startTime = hpcAccountDetail.getStartTime();
            Date endTime = hpcAccountDetail.getEndTime();
            long sumHpcTime = DateUtil.between(startTime, endTime, DateUnit.SECOND);
            List<DateScope> dateScopes1 = splitByDateUnit(startTime, endTime, displayUnit);
            // dateScopes1，可能被截断，比如 2021-06-01~2021-06-03,但是 条件为 2021-06-02～2021-06-05
            // 要处理为 2021-06-02～2021-06-03，只有单位在月以及周才会有这种情况
            // 根据条件再次处理，时间范围
            dateScopes1.forEach(dateScope -> {
                Date s = dateScope.getS();
                Date e = dateScope.getE();
                if (DateUtil.compare(s, startDate) < 0 && DateUtil.compare(s, endDate) < 0) {
                    dateScope.setS(startDate);
                }
                if (DateUtil.compare(e, startDate) > 0 && DateUtil.compare(e, endDate) > 0) {
                    dateScope.setE(endDate);
                }
            });
            for (DateScope dateScope1 : dateScopes1) {
                Date s = dateScope1.getS();
                Date e = dateScope1.getE();
                long between = DateUtil.between(s, e, DateUnit.SECOND);
                double scopeHpcCoreTime = sumHpcTime == 0 ? 0 : Arith.mul(Arith.div(between * 1d, sumHpcTime), sumHpcCoreTime);
                HpcAccountUsageBos.add(new HpcAccountUsageBo(s, e, scopeHpcCoreTime));
            }
            iterator.remove();
        }
        // 分段合并HPC使用明细
        dateScopes.forEach(dateScope -> {
            Date s = dateScope.getS();
            Date e = dateScope.getE();
            Iterator<HpcAccountUsageBo> usageBoIterator = HpcAccountUsageBos.iterator();
            while (usageBoIterator.hasNext()) {
                HpcAccountUsageBo hpcAccountUsageBo = usageBoIterator.next();
                // 消耗算力
                Double sumHpcCoreTime = hpcAccountUsageBo.getUsage();
                Date s1 = hpcAccountUsageBo.getStartDate();
                Date e1 = hpcAccountUsageBo.getEndDate();
                if ((DateUtil.compare(s1, s) >= 0 && DateUtil.compare(s1, e) < 0) &&
                    (DateUtil.compare(e1, s) > 0 && DateUtil.compare(e1, e) <= 0)) {
                    HpcAccountUsageBo hpcAccountUsageBo1 = hpcAccountUsageBoMap.get(s);
                    hpcAccountUsageBo1.setUsage(Arith.add(hpcAccountUsageBo1.getUsage(), sumHpcCoreTime));
                    usageBoIterator.remove();
                }
            }
        });
        return new ArrayList<>(hpcAccountUsageBoMap.values());
    }

    /**
     * 按时间单位，分割时间范围，startDate <= startDate < endDate
     *
     * @param startDate
     * @param endDate
     * @param displayUnit
     * @return
     */
    private List<DateScope> splitByDateUnit(Date startDate, Date endDate, DisplayUnit displayUnit) {
        List<DateScope> dateScopes = new ArrayList<>();
        do {
            Date e = getBeginOffsetTime(displayUnit, startDate, 1);
            if (DateUtil.compare(e, endDate) > 0) {
                if (!DateUtil.isSameTime(startDate, endDate)) {
                    dateScopes.add(new DateScope(startDate, endDate));
                }
                break;
            } else {
                dateScopes.add(new DateScope(startDate, e));
                startDate = getBeginOffsetTime(displayUnit, startDate, 1);
            }
        } while (true);
        return dateScopes;
    }

    @Data
    @AllArgsConstructor
    private class DateScope {
        private Date s;
        private Date e;
    }

    private Date getBeginOffsetTime(DisplayUnit displayUnit, Date startTime, int offset) {
        Date offsetTime;
        switch (displayUnit) {
            case M:
                offsetTime = DateUtil.offsetMonth(DateUtil.beginOfMonth(startTime), offset);
                break;
            case W:
                offsetTime = DateUtil.offsetWeek(DateUtil.beginOfWeek(startTime), offset);
                break;
            case H:
                offsetTime = DateUtil.offsetHour(DateUtil.beginOfHour(startTime), offset);
                break;
            case D:
            default:
                offsetTime = DateUtil.offsetDay(DateUtil.beginOfDay(startTime), offset);
                break;
        }
        return offsetTime;
    }

    private Date getEndOffsetTime(DisplayUnit displayUnit, Date date, int offset) {
        Date offsetTime;
        switch (displayUnit) {
            case M:
                offsetTime = DateUtil.offsetMonth(DateUtil.endOfMonth(date), offset);
                break;
            case W:
                offsetTime = DateUtil.offsetWeek(DateUtil.endOfWeek(date), offset);
                break;
            case H:
                offsetTime = DateUtil.offsetHour(DateUtil.endOfHour(date), offset);
                break;
            case D:
            default:
                offsetTime = DateUtil.offsetDay(DateUtil.endOfDay(date), offset);
                break;
        }
        return offsetTime;
    }

    /**
     * 转换日期查询参数，转换开始日期为当天初始时间，转换结束日期为当天结束时间
     *
     * @param startDate
     * @param endDate
     */
    private void convertDateQuery(Date startDate, Date endDate) {
        if (startDate != null) {
            startDate.setTime(DateUtil.beginOfDay(startDate).getTime());
        }
        if (endDate != null) {
            endDate.setTime(DateUtil.offsetDay(DateUtil.beginOfDay(endDate), 1).getTime());
        }
    }

    @Override
    public IPage<HpcAccountUsageWithIncreaseBo> listHpcAccountUsageWithIncreaseUsage(PageParam pageParam, List<String> recordIds, Date startDate, Date endDate) {
        // 获取查询的HPC账户
        HpcAccountRecordBo hpcAccountRecordBo = null;
        if (CollectionUtil.isNotEmpty(recordIds) && recordIds.size() == 1) {
            hpcAccountRecordBo = hpcAccountRecordService.get(recordIds.get(0));
        }

        // 间隔多少天就说明有多少条数据（查询出时间区间内机时使用明细）
        List<HpcAccountUsageBo> hpcAccountUsageBos = this.listHpcAccountUsage(recordIds, startDate, endDate, DisplayUnit.D);
        hpcAccountUsageBos.sort((o2, o1) -> DateUtil.compare(o1.getStartDate(), o2.getStartDate()));

        // 构造分页结果
        IPage<HpcAccountUsageWithIncreaseBo> page = new Page<>();
        List<HpcAccountUsageWithIncreaseBo> hpcAccountUsageWithIncreaseDtos = new ArrayList<>();
        page.setRecords(hpcAccountUsageWithIncreaseDtos);
        page.setTotal(hpcAccountUsageBos.size());
        page.setSize(pageParam.getSize());
        page.setCurrent(pageParam.getCurrent());

        // 设置页码从第一页开始
        PageUtil.setOneAsFirstPageNo();
        int[] pageIndexS = PageUtil.transToStartEnd((int) pageParam.getCurrent(), (int) pageParam.getSize());

        // 初始化一些数据
        for (int i = pageIndexS[0]; i < hpcAccountUsageBos.size() && i < pageIndexS[1]; i++) {
            HpcAccountUsageBo hpcAccountUsageBo = hpcAccountUsageBos.get(i);
            HpcAccountUsageWithIncreaseBo hpcAccountUsageWithIncreaseDto = MapperUtil.nf().map(hpcAccountUsageBo, HpcAccountUsageWithIncreaseBo.class);
            hpcAccountUsageWithIncreaseDto.setIncrease(0d);
            hpcAccountUsageWithIncreaseDto.setIncreaseTotal(0d);
            hpcAccountUsageWithIncreaseDto.setBalance(0d);
            hpcAccountUsageWithIncreaseDto.setDate(hpcAccountUsageBo.getStartDate());
            hpcAccountUsageWithIncreaseDtos.add(hpcAccountUsageWithIncreaseDto);
        }

        if (CollectionUtil.isEmpty(hpcAccountUsageWithIncreaseDtos)) return page;

        Date updateStartDate = null;
        Date updateEndDate = null;
        if (hpcAccountUsageWithIncreaseDtos.size() == 1) {
            updateStartDate = updateEndDate = hpcAccountUsageBos.get(0).getStartDate();
        } else if (hpcAccountUsageWithIncreaseDtos.size() > 1) {
            updateEndDate = hpcAccountUsageBos.get(0).getStartDate();
            updateStartDate = hpcAccountUsageBos.get(hpcAccountUsageBos.size() - 1).getStartDate();
        }

        List<HpcAccountIncreaseUsageBo> hpcAccountIncreaseUsage = listHpcAccountIncreaseUsage(recordIds, updateStartDate, updateEndDate);

        for (HpcAccountUsageWithIncreaseBo hpcAccountUsageWithIncreaseDto : hpcAccountUsageWithIncreaseDtos) {
            for (HpcAccountIncreaseUsageBo hpcAccountIncreaseDto : hpcAccountIncreaseUsage) {
                if (DateUtil.compare(DateUtil.beginOfDay(hpcAccountUsageWithIncreaseDto.getDate()), DateUtil.beginOfDay(hpcAccountIncreaseDto.getDate())) == 0) {
                    hpcAccountUsageWithIncreaseDto.setIncrease(hpcAccountIncreaseDto.getIncrease());
                    hpcAccountUsageWithIncreaseDto.setIncreaseTotal(hpcAccountIncreaseDto.getIncreaseTotal());

                    if (Objects.nonNull(hpcAccountRecordBo)) {
                        String hpcAccountRecordBoPid = hpcAccountRecordBo.getPid();
                        HpcAccountAmountDailyBo accountAmountDailyBo = new HpcAccountAmountDailyBo();
                        accountAmountDailyBo.setHpcAccountRecordId(hpcAccountRecordBoPid);
                        accountAmountDailyBo.setBusTime(DateUtil.beginOfDay(hpcAccountUsageWithIncreaseDto.getDate()));

                        // 判断是否为当日
                        if (DateUtil.compare(DateUtil.beginOfDay(hpcAccountUsageWithIncreaseDto.getDate()), DateUtil.beginOfDay(new Date())) == 0) {
                            // 当日消耗
                            Double toDayCoreTime = this.getHpcCoreSumByScopeDate(Arrays.asList(hpcAccountRecordBoPid), DateUtil.beginOfDay(new Date()), DateUtil.endOfDay(new Date()));
                            hpcAccountUsageWithIncreaseDto.setUsage(toDayCoreTime);
                            // 当日剩余
                            hpcAccountUsageWithIncreaseDto.setBalance(hpcAccountRecordBo.getCoreTimeBalance());
                        } else {
                            HpcAccountAmountDailyBo amountDailyServiceOne = hpcAccountAmountDailyService.getOne(accountAmountDailyBo);
                            if (amountDailyServiceOne != null) {
                                hpcAccountUsageWithIncreaseDto.setBalance(amountDailyServiceOne.getCurCoreTimeBalance());
                            }
                        }

                        hpcAccountUsageWithIncreaseDto.setHpcAccount(hpcAccountRecordBo.getHpcAccountSnapshot());
                    }
                    break;
                }
            }
        }

        return page;
    }

    @Override
    public List<HpcAccountIncreaseUsageBo> listHpcAccountIncreaseUsage(List<String> recordIds, Date startDate, Date endDate) {
        if (DateUtil.compare(startDate, endDate) > 0) return new ArrayList<>();

        List<HpcAccountIncreaseUsageBo> hpcAccountIncreaseDtos = new ArrayList<>();
        List<HpcAccountIncreaseBo> hpcAccountIncreaseBos = hpcAccountIncreaseService.findIncrease(recordIds);
        long totalDays = DateUtil.betweenDay(startDate, endDate, true) + 1;
        for (int i = 0; i < totalDays; i++) {
            Date indexDate = DateUtil.offsetDay(startDate, i);
            double increase = 0d;
            Iterator<HpcAccountIncreaseBo> iterator = hpcAccountIncreaseBos.iterator();
            HpcAccountIncreaseBo hpcAccountIncreaseBo;
            while (iterator.hasNext()) {
                hpcAccountIncreaseBo = iterator.next();
                // 比较日期字符串 (date与localDateTime)
                if (Objects.equals(DateUtil.formatDate(indexDate),
                    LocalDateTimeUtils.formatTime(hpcAccountIncreaseBo.getIncreaseDate(), DatePattern.NORM_DATE_PATTERN))) {
                    increase = Arith.add(increase, hpcAccountIncreaseBo.getSingleCount().doubleValue());
                    iterator.remove();
                }
            }

            HpcAccountIncreaseUsageBo hpcAccountUsageBo = new HpcAccountIncreaseUsageBo();
            hpcAccountUsageBo.setDate(indexDate).setIncrease(increase);

            hpcAccountIncreaseDtos.add(hpcAccountUsageBo);
        }
        dealIncreaseDto(hpcAccountIncreaseDtos, recordIds);
        return hpcAccountIncreaseDtos;
    }


    /**
     * 累加增值数据
     *
     * @param hpcAccountIncreaseDtos
     */
    private void dealIncreaseDto(List<HpcAccountIncreaseUsageBo> hpcAccountIncreaseDtos, List<String> recordIds) {
        if (CollectionUtil.isEmpty(hpcAccountIncreaseDtos)) return;
        HpcAccountIncreaseUsageBo hpcAccountIncreaseDto = hpcAccountIncreaseDtos.get(0);
        double increaseTotal = hpcAccountIncreaseService.getSumIncreaseByStartDate(recordIds, DateUtil.endOfDay(hpcAccountIncreaseDto.getDate()));
        hpcAccountIncreaseDto.setIncreaseTotal(increaseTotal);

        for (int i = 1; i < hpcAccountIncreaseDtos.size(); i++) {
            HpcAccountIncreaseUsageBo hpcAccountIncreaseDto1 = hpcAccountIncreaseDtos.get(i - 1);
            HpcAccountIncreaseUsageBo hpcAccountIncreaseDto2 = hpcAccountIncreaseDtos.get(i);
            hpcAccountIncreaseDto2.setIncreaseTotal(Arith.add(hpcAccountIncreaseDto2.getIncrease(), hpcAccountIncreaseDto1.getIncreaseTotal()));
        }
    }

    @Override
    public Date getMaxEndTimeByRecordIds(List<String> recordIds) {
        if (CollectionUtil.isEmpty(recordIds)) return null;

        QueryWrapper<HpcAccountDetail> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("MAX(end_time) as end_time").lambda()
            .eq(recordIds.size() == 1, HpcAccountDetail::getHpcAccountRecordId, recordIds.get(0))
            .in(recordIds.size() > 1, HpcAccountDetail::getHpcAccountRecordId, recordIds);

        HpcAccountDetail hpcAccountDetail = this.getOne(queryWrapper);

        return hpcAccountDetail == null ? null : hpcAccountDetail.getEndTime();
    }

    @Override
    public List<HpcAccountBillBO> listHpcAccountBill(String providerType, String keyword, Date startDate, Date endDate) {
        List<HpcAccountBillBaseBO> baseBOS = this.baseMapper.listHpcAccountByPeriod(providerType, keyword, startDate, endDate);
        List<HpcAccountBillBO> billBOS = MapperUtil.nf().mapAsList(baseBOS, HpcAccountBillBO.class);

        List<String> recordIds = billBOS.stream().map(HpcAccountBillBO::getHpcAccountRecordId).collect(Collectors.toList());
        List<HpcAccountDetail> hpcAccountDetails = listPeriodDetailsByRecordIds(recordIds, startDate, endDate);
        Map<String, List<HpcAccountDetail>> detailMap = hpcAccountDetails.stream().collect(Collectors.groupingBy(HpcAccountDetail::getHpcAccountRecordId));

        billBOS.forEach(billBO -> {
            billBO.setTotalBalance(NumberUtil.round(billBO.getTotalBalance(), 2).doubleValue());
            billBO.setTotalCoreTime(NumberUtil.round(billBO.getTotalCoreTime(), 2).doubleValue());
            billBO.setTotalUsage(NumberUtil.sub(billBO.getTotalCoreTime(), billBO.getTotalBalance()));

            List<HpcAccountDetail> details = detailMap.get(billBO.getHpcAccountRecordId());
            if (details != null) {
                double billPeriodUsage = details.stream()
                    .mapToDouble(detail -> NumberUtil.div((detail.getEndTime().getTime() - detail.getStartTime().getTime()), 3600 * 1000 * detail.getHpcCore()))
                    .sum();
                billBO.setBillPeriodUsage(NumberUtil.round(billPeriodUsage, 2).doubleValue());
            } else {
                billBO.setBillPeriodUsage(0.00);
            }

        });

        return billBOS;
    }

    @Override
    public List<HpcAccountDetailBo> listHpcAccountBillTasks(List<String> hpcAccountRecordIds, Date startDate, Date endDate) {
        List<HpcAccountDetail> hpcAccountDetails = listPeriodDetailsByRecordIds(hpcAccountRecordIds, startDate, endDate);
        return MapperUtil.nf().mapAsList(hpcAccountDetails, HpcAccountDetailBo.class);
    }

    private List<HpcAccountDetail> listPeriodDetailsByRecordIds(List<String> hpcAccountRecordIds, Date startDate, Date endDate) {
        List<HpcAccountDetail> hpcAccountDetails = this.listDetailsByRecordIds(hpcAccountRecordIds, startDate, endDate);
        hpcAccountDetails.forEach(detail -> {
            if (DateUtil.compare(startDate, detail.getStartTime()) > 0) {
                detail.setStartTime(startDate);
            }
            if (DateUtil.compare(endDate, detail.getEndTime()) < 0) {
                detail.setEndTime(endDate);
            }
        });
        return hpcAccountDetails;
    }
}
